#include "collision_detector.h"

CollisionDetector::CollisionDetector(void)
{
	
}

/* controlla i tipi di entita' e chiama i metodi specializzati (e possibilmente
 * ottimizzati) che gestiscono le collisioni in base al tipo degli oggetti */
CollisionInfo *CollisionDetector::resolveCollision(WorldEntity *entity1, WorldEntity *entity2)
{
	if (entity1->m_type == TYPE_PLAYER && entity2->m_type == TYPE_MAP_AREA)
		return resolveCollision((WorldPlayer*)entity1, (WorldArea*)entity2);
	else if (entity1->m_type == TYPE_MAP_AREA && entity2->m_type == TYPE_PLAYER)
		return resolveCollision((WorldPlayer*)entity2, (WorldArea*)entity1);
	else if (entity1->m_type == TYPE_MAP_AREA && entity2->m_type == TYPE_PROJECTILE)
		return resolveCollision((WorldProjectile*)entity2, (WorldArea*)entity1);
	else if (entity1->m_type == TYPE_PROJECTILE && entity2->m_type == TYPE_MAP_AREA)
			return resolveCollision((WorldProjectile*)entity2, (WorldArea*)entity1);
	
	/* il caso collisione tra un'area ed un'altra per ora non ha senso di esistere,
	 * almeno si risparmiano un po' di calcoli */
	return '\0';
}

void CollisionDetector::orthoMinMax(GLfloat vertexs[8], float &top, float &bottom, float &left, float &right)
{
	/* se x1 == x2 -> devono avere ordinata diversa: le impostiamo*/
	if (vertexs[0] == vertexs[2])
	{
		if (vertexs[1] < vertexs[3])
		{
			bottom = vertexs[1];
			top = vertexs[3];
		}
		else
		{
			bottom = vertexs[3];
			top = vertexs[1];
		}
		
		if (vertexs[2] < vertexs[4])
		{
			left = vertexs[2];
			right = vertexs[4];
		}
		else
		{
			left = vertexs[4];
			right = vertexs[2];				
		}
	}
	/* se x1 != x2 impostiamo direttamente queste */
	else
	{
		if (vertexs[0] < vertexs[2])
		{
			left = vertexs[0];
			right = vertexs[2];
		}
		else
		{
			left = vertexs[2];
			right = vertexs[0];
		}

		if (vertexs[3] < vertexs[5])
		{
			bottom = vertexs[3];
			top = vertexs[5];
		}
		else
		{
			bottom = vertexs[5];
			top = vertexs[3];				
		}		
	}
}

/* risolve le collisioni tra un proiettile e un'area del gioco */
CollisionInfo *CollisionDetector::resolveCollision(WorldProjectile *entity1, WorldArea *entity2)
{
	/* TODO: se il poligono ha 4 vertici (per ora si suppone sia ortogonale per forza, poi dovremo raffinare) */
	if (entity2->numVertexs == 8)
	{
		/* coordinate dell'area */
		GLfloat areaLeft, areaRight, areaTop, areaBottom;
		
		orthoMinMax(entity2->vertexs, areaTop, areaBottom, areaLeft, areaRight);
		
		if (entity1->x > areaLeft && entity1->x < areaRight && entity1->y > areaBottom && entity1->y < areaTop)
		{
			return new ProjectileOrthoCollision(true, 0.0f, 0.0f, NONE);
		}
	}
	
	return new ProjectileOrthoCollision(false, 0.0f, 0.0f, NONE);
}

/* risolve le collisioni tra un player ed un poligono */
CollisionInfo *CollisionDetector::resolveCollision(WorldPlayer *entity1, WorldArea *entity2)
{
	/* per le collisioni l'algoritmo e' il seguente:
	 * - per ogni lato dei due poligoni
	 *   - trova l'asse perpendicolare al lato
	 *   - proietta entrambi i poligoni su quest'asse
	 *   - se le proiezioni si sovrappongono: COLLISIONE!
	 * 
	 * volendo gestire anche la predizione basta sommare la velocita' alla posizione
	 * e fare il calcolo :v
	 */
	
	/* TODO: gestire poligoni storti 
	 * 
	 	for (int i = 0; i < entity2->numVertexs; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				GLfloat edge1_x = entity2->vertexs[2*i];
				GLfloat edge1_y = entity2->vertexs[(2*i)+1];
			}
			 
		}
	*/
	
	/* TODO: se il poligono ha 4 vertici (per ora si suppone sia ortogonale per forza, poi dovremo raffinare) */
	if (entity2->numVertexs == 8)
	{
		/* coordinate dell'area */
		GLfloat areaLeft, areaRight, areaTop, areaBottom;

		orthoMinMax(entity2->vertexs, areaTop, areaBottom, areaLeft, areaRight);
		
		GLfloat playerLeft, playerRight, playerTop, playerBottom;
		
		playerLeft = entity1->x - entity1->width/2;
		playerRight = entity1->x + entity1->width/2;
		playerTop = entity1->y + entity1->height/2;
		playerBottom = entity1->y - entity1->height/2;
		

		
		/* TODO: ancora non funziona roba */
		
		/* se non troviamo un separating axis orizzontale */
		if ((playerBottom < areaTop) && (playerTop > areaBottom))
		{				
			/* down */
			if (playerRight > areaLeft && playerLeft < areaRight)
			{		
					/* calcola l'adjustment del giocatore per farlo collidere
					 * preciso al pavimento */
					if (entity1->velocity.y < 0.0f)
					{
						float depth = areaTop - playerBottom;
		
						return new PlayerOrthoCollision(true, DOWN, depth);
					}
			}
			/* angolo basso dx */
			else if (playerRight > areaLeft)
			{
				
			}
			/* angolo basso centro */
			else if (playerLeft < areaRight)
			{

			}
			
		}

		/* TODO: controllare se e' corretto */
		if ((playerTop > areaBottom) && (playerBottom > areaBottom))
		{	
			/* up */
			if (playerRight > areaLeft && playerLeft < areaRight)
			{		
					/* calcola l'adjustment del giocatore per farlo collidere
					 * preciso al pavimento */
					if (entity1->velocity.y > 0.0f)
					{						
						/* TODO: NON FUNZIONA UN CAZZO */
						float depth = playerTop - areaBottom;

						//return new PlayerOrthoCollision(true, UP, depth);
					}
			}
			/* angolo basso dx */
			else if (playerRight > areaLeft)
			{

			}
			/* angolo basso centro */
			else if (playerLeft < areaRight)
			{

			}		
		}
		
		return new PlayerOrthoCollision(false, NONE, 0.0f);
	}
	 
	return new PlayerOrthoCollision(false, NONE, 0.0f);
}

CollisionInfo::CollisionInfo(void)
{
	//
}

CollisionInfo::CollisionInfo(bool wC)
{
	willCollide = wC;
}

CollisionInfo::CollisionInfo(const CollisionInfo &copy)
{
	m_type = copy.m_type;
}

CollisionInfo::~CollisionInfo(void)
{
	
}

PlayerOrthoCollision::PlayerOrthoCollision(bool wC, char side, float depth) : CollisionInfo(wC)
{
	m_type = ORTHO_COLLISION;
	this->m_side = side;
	this->m_depth = depth;
}

PlayerOrthoCollision::PlayerOrthoCollision(const PlayerOrthoCollision &copy)
{
	m_type = copy.m_type;
	willCollide = copy.willCollide;
	m_side = copy.m_side;
	m_depth = copy.m_depth;
}

ProjectileOrthoCollision::ProjectileOrthoCollision(bool wC, float x, float y, char side) : CollisionInfo(wC)
{
	m_type = ORTHO_COLLISION;
	m_x = x;
	m_y = y;
	m_side = side;
}

ProjectileOrthoCollision::ProjectileOrthoCollision(const ProjectileOrthoCollision &copy)
{
	willCollide = copy.willCollide;
	m_type = copy.m_type;
	m_x = copy.m_x;
	m_y = copy.m_y;
	m_side = copy.m_side;
}

ProjectileOrthoCollision::~ProjectileOrthoCollision(void)
{
	
}

